﻿using System;
using System.Linq;
using Sendz.Domain;
using System.Security.Cryptography;
using System.Text;
using System.Collections.Generic;

namespace Sendz.Model
{
    public class EntityUserRepository : IUserRepository
    {
        private string MD5Encrypted(string unencrypted)
        {
            // 1-way encryption for passwords
            var encrypt = new MD5CryptoServiceProvider();
            var data = Encoding.ASCII.GetBytes(unencrypted);
            data = encrypt.ComputeHash(data);
            return Encoding.ASCII.GetString(data);
        }

        // Users

        // Create a non-finalized user account for Beta or temporary usage
        public Guid CreateTempUser(Guid userKey, string name, string email, Guid invitedByUserKey)
        {
            using (SendzContext db = new SendzContext())
            {
                var newUser = new User(userKey, email, invitedByUserKey);
                // Allow a name if one was provided (and try a logical parse--user can fix), keep other values temporary
                if (!string.IsNullOrWhiteSpace(name))
                {
                    var arrName = name.Split(' ');
                    var lastNameBuilder = new StringBuilder(string.Empty);
                    for (var i = 0; i < arrName.Count(); i++)
                    {
                        if (i == 0)
                            newUser.Firstname = arrName[i];
                        else
                        {
                            if (i == 1)
                                lastNameBuilder.Append(arrName[i]);
                            else
                                lastNameBuilder.Append(" " + arrName[i]);
                        }
                    }
                    newUser.Lastname = lastNameBuilder.ToString();
                }
                else
                {
                    newUser.Firstname = string.Empty;
                    newUser.Lastname = string.Empty;
                }
                newUser.Username = string.Empty;
                newUser.Password = string.Empty;
                newUser.Zipcode = string.Empty;
                db.Users.AddObject(newUser);
                db.SaveChanges();
                return newUser.UserKey;
            }
        }

        public Guid TryLoginGetUserKey(string username, string password)
        {
            using (SendzContext db = new SendzContext())
            {
                var encryptedPassword = MD5Encrypted(password);
                var tryUser = db.Users.FirstOrDefault(u => u.Username == username && u.Password == encryptedPassword && !u.IsRemoved);
                if (tryUser != null)
                {
                    tryUser.LastLogin = DateTime.Now;
                    db.SaveChanges();
                    return tryUser.UserKey;
                }
                return Guid.Empty;
            }
        }

        public User GetUserByName(string username)
        {
            using (SendzContext db = new SendzContext())
            {
                return db.Users.FirstOrDefault(u => u.Username == username);
            }
        }

        public User GetUserByEmail(string email)
        {
            using (SendzContext db = new SendzContext())
            {
                return db.Users.FirstOrDefault(u => u.Email == email);
            }
        }

        public List<User> GetUsers()
        {
            using (SendzContext db = new SendzContext())
            {
                return db.Users.Where(u => !u.IsRemoved).OrderBy(u => u.Lastname).ThenBy(u => u.Firstname).ToList();
            }
        }

        public bool SaveUser(User user)
        {
            using (SendzContext db = new SendzContext())
            {
                try
                {
                    var thisUser = db.Users.FirstOrDefault(u => u.UserKey == user.UserKey);
                    if (thisUser != null)
                    {
                        thisUser.Username = user.Username;
                        // This is not where password updates occur
                        thisUser.Firstname = user.Firstname;
                        thisUser.Lastname = user.Lastname;
                        thisUser.Email = user.Email;
                        thisUser.Zipcode = user.Zipcode;
                        thisUser.ResponsesRemaining = user.ResponsesRemaining;
                        thisUser.InvitesRemaining = user.InvitesRemaining;
                        thisUser.IsAdmin = user.IsAdmin;
                        // Update the last modified date
                        thisUser.LastModified = DateTime.Now;
                    }
                    else
                    {
                        // Update the last modified date so new users can also appear at top of sort order
                        user.LastModified = DateTime.Now;
                        user.Password = MD5Encrypted(user.Password);
                        db.Users.AddObject(user);
                    }
                    db.SaveChanges();
                    return true;
                }
                catch (Exception ex)
                {
                    throw ex;
                    return false;
                }
            }
        }

        public User GetUserByKey(Guid userKey)
        {
            using (SendzContext db = new SendzContext())
            {
                return db.Users.FirstOrDefault(u => u.UserKey == userKey);
            }
        }

        public bool RemoveUser(Guid userKey)
        {
            using (SendzContext db = new SendzContext())
            {
                try
                {
                    var thisUser = db.Users.Single(u => u.UserKey == userKey);
                    // Logical delete, not physical delete
                    thisUser.IsRemoved = true;
                    // Update the last modified date
                    thisUser.LastModified = DateTime.Now;
                    db.SaveChanges();
                    return true;
                }
                catch
                {
                    return false;
                }
            }
        }

        //TODO: KEEP DOING USING

        public bool ResetUserPassword(Guid userKey, string newPassword)
        {
            using (SendzContext db = new SendzContext())
            {
                try
                {
                    var thisUser = db.Users.Single(u => u.UserKey == userKey);
                    // Reset password to something encrypted
                    thisUser.Password = MD5Encrypted(newPassword);
                    // Update the last modified date
                    thisUser.LastModified = DateTime.Now;
                    db.SaveChanges();
                    return true;
                }
                catch
                {
                    return false;
                }
            }
        }

        public bool UpdateUserResponses(Guid userKey, int newResponses)
        {
            using (SendzContext db = new SendzContext())
            {
                try
                {
                    var thisUser = db.Users.Single(u => u.UserKey == userKey);
                    // Update responses
                    thisUser.ResponsesRemaining = newResponses;
                    // Update the last modified date
                    thisUser.LastModified = DateTime.Now;
                    db.SaveChanges();
                    return true;
                }
                catch
                {
                    return false;
                }
            }
        }

        public bool UpdateUserInvites(Guid userKey, int newInvites)
        {
            using (SendzContext db = new SendzContext())
            {
                try
                {
                    var thisUser = db.Users.Single(u => u.UserKey == userKey);
                    // Update invites
                    thisUser.InvitesRemaining = newInvites;
                    // Update the last modified date
                    thisUser.LastModified = DateTime.Now;
                    db.SaveChanges();
                    return true;
                }
                catch
                {
                    return false;
                }
            }
        }

        public bool IsEmailValidated(Guid userKey, string email)
        {
            using (SendzContext db = new SendzContext())
            {
                email = email.ToLowerInvariant();
                return (db.UserEmailValidations.FirstOrDefault(uev => uev.UserKey == userKey && uev.ValidationEmail == email && uev.IsValidated) != null) ? true : false;
            }
        }

        public Guid CreateEmailValidation(Guid userKey, string email)
        {
            using (SendzContext db = new SendzContext())
            {
                try
                {
                    var newEmailValidation = new UserEmailValidation(userKey, email.ToLowerInvariant());
                    db.UserEmailValidations.AddObject(newEmailValidation);
                    db.SaveChanges();
                    return newEmailValidation.ValidationKey;
                }
                catch
                {
                    return Guid.Empty;
                }
            }
        }

        public bool ValidateEmailByKey(Guid validationKey)
        {
            using (SendzContext db = new SendzContext())
            {
                try
                {
                    var thisValidation = db.UserEmailValidations.FirstOrDefault(uev => uev.ValidationKey == validationKey && uev.IsValidated == false);
                    if (thisValidation != null)
                    {
                        thisValidation.IsValidated = true;
                        thisValidation.LastModified = DateTime.Now;
                        db.SaveChanges();
                        return true;
                    }
                    return false;
                }
                catch
                {
                    return false;
                }
            }
        }
    }
}
